/*  
  -- The Lime II Project -- 

  A tuplespaces-based middleware for coordinating agents and mobile hosts.
  Copyright (C) 2005.
  Gian Pietro Picco, Amy L. Murphy, Lorenzo Bellini.

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  Lesser General Public License for more details.

  You should have received this copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 
*/

package lime2;
import lights.adapters.*;
import lights.interfaces.*; 

import java.util.Vector;


/**
 * Controls access to the underlying tuplespace engine. 
 * <br>
 * <br>
 * <b><i>Why a Proxy ?</i></b>
 * <br>
 * <br>
 * Basically TupleSpaceProxy adds a (lightweight) layer of abstraction to a concrete tuplespace engine. In the 
 * current implementation we are using this layer only to enforce the correct semantics for the match-all-tuples 
 * template wrt limified tuples. But in the future we could use this class as an "adapter" towards more 
 * sophisticated features such as security, load balancing, caching, replication, etc... 
 * <br>
 * The TupleSpaceProxy is accessed by the reactor only; just the methods of the ITupleSpace interface that are 
 * really needed by the reactor are implemented. The other ones just throw LimeErrors. For instance the blocking 
 * operations "in" and "rd" are implemented by the LimeTupleSpace using system reactions; "in" and "rd" of the 
 * underlying tuplespace engine are NEVER used. 
 * <br>
 * <br>
 * <b><i>Match-All-Limified-Tuples Template</i></b>
 * <br>
 * <br>
 * Why enforcing a match-all-tuples template with the correct semantics wrt limified tuples is so difficult ?
 * <br>
 * The answer requires to discuss the inner levels of the Lime middleware. 
 * <br>
 * <br>
 * Basically reactor is the only class in which matching of tuples are performed. In LighTS specifications 
 * <i>tupleA.matches(tupleB)</i> must be true for any <i>tupleB</i> if <i>tupleA</i> has zero length (= empty tuple);
 * please note that LighTS API doesn't state anything about what should happen if <i>tupleA</i> has non zero length 
 * and <i>tupleB</i> has length zero (see the <b><i>Asymmetric Equality</i></b> comment).
 * <br>
 * Anyway in Lime internals the minimum length of an ITuple is 3 (and not 0) because of limified ITuples: reactor 
 * is the object that stores the ITuples; higher layers of the Lime system take care of passing to it only limified 
 * ITuples. Let's consider <i>tupleA.matches(tupleB)</i> method of LighTS when:
 * <P><i>
 * tupleA = new LimeTemplate(id, cur, dest, new Tuple()).limify()
 * <br>
 * tupleB = new LimeTuple(id, cur, dest, new Tuple().add(x)).limify()
 * </i></P>
 * <i>tupleA</i> is not an ITuple with length 0 and it has length 3 whereas <i>tupleB</i> has length 4, so false is 
 * returned. But it would be semantically correct to return true instead, since:
 * <P>
 * &#9679 <i>id</i>, <i>cur</i>, <i>dest</i> match
 * <br>
 * &#9679 <i>new Tuple()</i> of <i>tupleA</i> is a match-all-tuples template
 * </i></P>
 * To partially fix this problem we could introduce the following method in class Reactor (or LimeTupleSpace):
 * <P><code>
 * static boolean matchTuples(ITuple template, ITuple tuple)
 * <br>
 * {
 * <br>
 * &#160 if (template.length() != 3) return template.matches(tuple);
 * <br>
 * &#160 else 
 * <br>
 * &#160 { 
 * <br>
 * &#160 &#160 ITuple aux = LimeTupleSpace.copy(0, TAGS_NUMBER, tuple);
 * <br>
 * &#160 &#160 return template.matches(aux);
 * <br>
 * &#160 }
 * <br>
 * }
 * </code></P>
 * that implements the <u>correct match ALGORITHM for limified ITuples</u>:
 * <br>
 * <br>
 * <i>
 * if the template has only three fields, return the result of default "matches" with a new tuple consisting only
 * of the first three fields of the original tuple, otherwise return the result of default "matches" with the 
 * original tuple.
 * </i>
 * <br>
 * <br>
 * Why "partially" fix ? Because reactor needs to invoke rd, in, rdg, ing on its internal ITuplespace object and 
 * these methods would internally use the default "lights.adapters.Tuple.matches" method that, as we have said, 
 * is not correct wrt the  semantics of limified tuples. 
 * <br>
 * <br>
 * </i></P>
 * A smart solution would be decoupling the tuple class used by the application programmers wrt the tuple class 
 * used by the Lime system. In the current version of Lime, the Lime system uses the same tuple class that the 
 * application programmers chose when they set the concrete TupleSpaceFactory.
 * We would need to change in LimeTuple and LimeTemplate in this way:
 * <P><code>
 * TupleID id
 * <br>
 * Localion cur
 * <br>
 * Location dest
 * <br>
 * ITuple tuple </code><b>must become "RTuple tuple"</b> 
 * </P>
 * where RTuple is a class implementing ITuple but whose matches method implements the correct semantics wrt to 
 * limified ITuples. Also the "limify" and "delimify" methods must be changed so that they automatically take care
 * of changing on the fly lights.adapters.Tuple in RTuples and vice-versa.
 * <br>
 * <br>
 * But this solution will not work, too. Why ? We need to recap how LighTS works.
 * <br>
 * <br>
 * LighTS makes use of two layers of adapters: a high level adapter (<i>lights.adapters</i>) wraps an instance of a 
 * low level adapter (<i>lights.adapters.builtin</i>, <i>lights.adapters.tspaces</i>), and the the low level adapter
 * wraps an instance of the tuple class the specific engine needs.
 * <br>
 * LighTS is not designed to provide "deep" interoperability: that would be, in some sense, meaningless.
 * Why ? The design of LighTS is based on the idea that programmers set a concrete TupleSpaceFactory at the very
 * beginning of their program, so that they use that factory. This "single factory" assumption implies that, 
 * although the methods of the adapters accept ITuples/IFields, the ITuples/IFields passed to them must be of the
 * same class of the adapters itself (because casting is performed).
 * <br> 
 * Is this a severe limitation ? No, it would be possible to provide interoperability on the basis of the 
 * interfaces defined by LighTS: instead of casting, we could extract and copy the Serializable/IFields within the
 * IField/ITuple parameter in order to instantiate a new field/tuple adapter of the proper class.  
 * <br>
 * Anyway, this solution would be meaningless. Why ? Because at the lowest level we have a concrete tuplespace
 * engine which requires and returns tuple instances of its <u>proprietary</u> class. So, all the information about
 * the ITuple class the programmer used, is ultimately lost when the tuple is stored in the tuplespace engine.
 * Of course, there are solutions for this problem, too - for example, inserting as the first field of the tuple
 * the information about the original ITuple class of the tuple. Then we could force the adapters to convert on the
 * fly the instances to return.
 * <br>
 * But this doesn't solve the key problem: the tuplespace engine has to make use of its proprietary tuple class;
 * to modify the internal behavior of the tuplespace engine (= force it to use another "matches" method for tuples)
 * we would need to create RTuple as a subclass of the proprietary tuple class.
 * This would require:
 * <P>
 * &#9679 subclassing the proprietary tuple class for EACH tuplespace engine 
 * <br>
 * &#9679 violating the "layered system" assumption 
 * <br>
 * &#9679 AND it doesn't really guarantee anything: what if the tuplespace engine invokes within his tuplespace class
 * an optimized private match method ? 
 * </P>
 * So, the best solution is to use a TupleSpaceProxy; let's consider how it works when it is asked to perform a "rdp"
 * operation for example:
 * <P><code>
 * public ITuple rdp(ITuple template) throws TupleSpaceException
 * <br>
 * {
 * <br>
 * &#160 if (template.length() != 3) return ts.rdp(template);
 * <br>
 * &#160 else return filterOne(template, ts.rdg(new Tuple()));
 * <br>
 * }
 * </code><P>
 * if the template is not a match-all-limified-tuples template, the tuplespace engine is required to perform the 
 * standard read. Otherwise the tuplespace engine is required to read all the tuples (we use the default 
 * match-all-tuples template for NON limified tuples), and filter them using the match method for limified tuples. 
 * <br>
 * Please note that:
 * <P>
 * &#9679 we don't really need LighTS to provide a match-all-tuples template; see <b><i>LighTS (Pre)Requisites</i></b>
 * <br>
 * &#9679 there is little overhead in doing this, since we expect the majority of the tuples within a tuplespace 
 * on host X to have that X as a destination location... queries for limified tuples (that is to say, encompassing 
 * the notion of tuple ID, current / destination location) are not much (locally) heavier than queries for standard 
 * tuples    
 * </P> 
 * An alternative solution would be: use RTuples and implement in Lime a simple tuplespace engine, which just stores 
 * ITuples in a linked list and operates on them just using the ITuple methods. But then the application programmers'
 * choice of the tuplespace engine to use would be pratically useless. Also an engine of this kind, because it 
 * provides maximum interoperability, would provide minimum features. I.e., the tuplespace engine which is builtin in
 * LighTS would be much better, since it provides an optimization for SPEED or storage SPACE based on serialization. 
 * <br>
 * <br>
 * <b><i>LighTS (Pre)Requisites</i></b>
 * <br>
 * <br>
 * <i>(1) Matches Method</i>
 * <br>
 * LighTS must provide a matching method according to these (general) rules:
 * <br>
 * <br>
 * <code>
 * <b>Field "matches"</b>
 * <br>
 * this.matches(f) returns true if: 
 * <br>
 * &#160 this and f are both actuals, they have the same type and value 
 * <br>
 * &#160 either this or f is a formal, and both have the same type 
 * </code>
 * <br>
 * <br>
 * This matching rule does not allow for matching based on subtypes.
 * <br>
 * <br>
 * <code>
 * <b>ITuple "matches"</b>
 * <br>
 * this.matches(t) returns true if: 
 * <br>
 * &#160 this and t have the same number of fields 
 * <br>
 * &#160 and 
 * <br>
 * &#160 all the fields of this match the corresponding field of t 
 * <br>
 * or 
 * <br>
 * &#160 this has no fields.
 * <br>
 * <br>
 * </code> 
 * This latter rule expresses the convention to consider tuples with no fields as templates that match any tuple, 
 * independently from the type and number of fields. Please note that the definition of matching wrt empty tuples
 * is asymmetrical: the template can be an empty tuple, but what about the target tuple to match ? (see 
 * <b><i>Asymmetrical Equality</i></b>).
 * <br>
 * <br>
 * <i>(2) Match-All-Tuples Template</i>
 * <br>
 * For the match-all-tuples template to be enabled in Lime, we don't need the match-all-tuples template to be
 * enabled for the underlying tuplespace engine. For example, the TupleSpaceProxy can store in a hashtable all
 * the different tuple structures of the tuples that are "outed" in the tuplespace, i.e. [Integer, String] or
 * [Integer, Integer, Integer]. When a match-all-tuples template operation is requested, then the TupleSpaceProxy
 * should perform the query for each recorded tuple structure, and then return the union of all the results. 
 * Luckily LighTS specifications <u>guarantee</u> (see <i>Matches Method</i>) the existence of match-all-tuple 
 * template for each supported tuplespace engine.        
 * <br>
 * A related topic LighTS specifications do <u>not</u> deal with is whether a match-all-tuples template can be 
 * inserted in a tuplespace just as if it were normal tuples: see <b><i>Asymmetrical Equality</i></b>.
 * <br>  
 * <br>
 * <i>(3) Deep Copy</i>
 * <br>
 * Unluckily LighTS specifications <u>don't guarantee</u> that the tuples returned by an "in" / "inp" / "rd" / etc..
 * operation are <i>deep copies</i> of the tuples in the tuplespace. 
 * <br>
 * We would like that if a tuple coming from a read operation is modified, the original tuple in the tuplespace is
 * NOT modified, too. So, for tuplespace engines that don't use deep copying, the corresponding LighTS adapters
 * "should" enforce this semantics by deep coyping tuples before returning them (in "read" operations).
 * Another solution would be to force the TupleSpaceProxy to "deep copy" the tuples before returning them; but in 
 * this case we could incur in the extra overhead of deep copying tuples that are already deep copies.
 * <br>
 * Please note that deep copying should be the default behavior for Linda clones: the builtin tuplespace engine of 
 * LighTS enforces it. IBM TSpaces also enforces it but there's a BUG (see documentication of LighTS within this 
 * distribution of Lime II). 
 * <br>
 * <br>
 * <i>(4) Naming</i>
 * <br>
 * LighTS doesn't state rules that define the legal names for tuplespaces. It just delegates to the concrete tuplespace
 * engine the creation of a tuplespace with the name passed by the user. But different tuplespace engines could have
 * different naming rules, i.e. TSpaces doesn't allow to create a tuplespace using a name that contains characters
 * ' ', '-', ':', etc..
 * <br>
 * In general a name passed by the user should undergo tuplespace engine specific transformations that make it legal.
 * These (transparent) transformations must also guarantee that if two names are different they will be mapped to 
 * different valid names. Although TupleSpaceProxy could define a <i>general</i> transformation that works for (a 
 * subset of ?) the tuplespace engines, it should be LighTS to enforce the tuplespace engine <i>specific</i> 
 * transformations.
 * <br>
 * The current solution is having TupleSpaceProxy to examine the tuplespace name: if the name is not "standard" enough
 * (= contains only Unicode letters/digits and is shorter than the NAME_LENGHT_LIMIT defined in LimeConstants) it
 * refuses to create the concrete tuplespace and throws a NamingException.
 * <br>
 * <br>
 * <b><i>Dealing with Multiple Servers on Localhost</i></b>
 * <br>
 * <br>
 * Please note that if the TupleSpaceProxy is asked to create a tuplespace with name "x", it creates a concrete 
 * tuplespace with name "x" + "port". Why ? The reason is simple: suppose you are running several LimeServers on  
 * localhost, then each of them will use a different unicast port. And each LimeServer will have its own 
 * TupleSpaceProxy, of course.
 * Now, if the underlying tuplespace engine is initialized so that a new runtime is started for each TupleSpaceProxy,
 * using just "x" would be fine. But the common case is having only one runtime support for the undelying tuplespace 
 * engine (i.e., you want to run only ONE instance of the TSpaces server).
 * In this case using just "x" would result in all the TupleSpaceProxies connecting to the runtime support and 
 * using the same concrete tuplespace with name "x". We don't want "true", low-level sharing of a tuplespace among 
 * different LimeServers on localhost: we want them to share the tuplespace only by means of the "abstractions" 
 * Lime itself provides. The non-conflicting name "x" + "unicast port" guarantees this behavior. 
 * <br>
 * Please note that the considerations of paragraph <i>Naming</i> still hold.
 * <br>
 * <br>
 * <b><i>Asymmetric Equality</i></b>
 * <br>
 * <br>
 * We always match ITuple against ITuple, meaning that the first ITuple is the template. And we refine LighTS 
 * semantics: not only we don't allow the matching to be true when the second ITuple is the match-all-tuples template,
 * also we force - at the TupleSpaceProxy level - match-all-tuples templates NOT to be inserted in a tuplespace: an 
 * IllegalTupleException is thrown instead. In fact this would result in having tuples in the tuplespace that react to
 * every reaction, which is different from having reactions that react on every tuple.  
 * 
 * @author Gian Pietro Picco
 * @author Amy Murphy
 * @author Lorenzo Bellini
 */

class TupleSpaceProxy implements ITupleSpace
{
 // private constant: LighTS guarantees that the empty tuple can be used as a match-all-tuples template
 final private Tuple MATCHALL = new Tuple();
	
 // private member variable	
 private TupleSpace ts = null;	
  
 /**
  * Constructor method.
  * 
  * @param name of the tuplespace
  * @throws NamingException
  * @throws TupleSpaceException
  */
 public TupleSpaceProxy(String name) throws TupleSpaceException
 {		
  // please see comments "Dealing with Multiple .." and "Naming"	
  if (name.length() > LimeConstants.NAME_LENGTH_LIMIT) throw new NamingException("a tuplespace name must be shorter than " + LimeConstants.NAME_LENGTH_LIMIT + " characters");	
  boolean legal = true;
  for (int i = 0; legal && i < name.length(); i++) legal = Character.isLetterOrDigit(name.charAt(i)); 
  if (!legal) throw new NamingException("only Unicode letters and digits are allowed in a tuplespace name"); 
  // .. just a runtime exception: we don't want the system to halt
  String trasf = name + LimeServer.getServer().getUnicastPort();
  // creates the (adapted) tuplespace 
  ts = new TupleSpace(trasf);
 }
 
 /**
  * Method getName. THIS METHOD IS NOT USED BY THE REACTOR.
  *  
  * @return name of the tuplespace
  */
 public String getName() 
 {
  throw new LimeError("invalid TupleSpaceProxy call");	
 }

 /**
  * Method count. THIS METHOD IS NOT USED BY THE REACTOR.
  *  
  * @param template
  * @return number of tuples matching template
  */
 public int count(ITuple template) 
 {
  throw new LimeError("invalid TupleSpaceProxy call");	
 }
 
 /**
  * Tests whether the given tuple is a match-all-template for limified tuples, that is to say a 
  * "match-all-limified-tuples" template.
  * 
  * modified by xing jiankuan at 2008-7-22 to support multiple fields 
  * @param t ITuple
  * @return boolean
  */
 public static boolean isMatchAllTemplate(ITuple t) {
		if (t.length() == LimeConstants.TAGS_NUMBER)
			return true;
		if (t.length() == LimeConstants.TAGS_NUMBER + 1) {
			IValuedField vf = (IValuedField) t.get(LimeConstants.TAGS_NUMBER);
			if (vf.equals(lights.dracots.Field.MATCH_ALL_FIELD))
				// <tid?, curr?, dest?, _*_>
				return true;
		}
		return false;
	}
 
 /**
  * Out. Please note that match-all-tuples templates cannot be "outed" in the tuplespace. 
  * 
  * @param tuple 
  * @throws IllegalTupleException
  * @throws TupleSpaceException
  */
 public void out(ITuple tuple) throws TupleSpaceException
 {
  // previous parameter checking guarantees tuple not to be null
  if (isMatchAllTemplate(tuple)) throw new IllegalTupleException("out of match-all-tuples template not allowed");
  ts.out(tuple);
 }
 
 /**
  * Outg. Please note that match-all-tuples templates cannot be "outed" in the tuplespace. 
  * 
  * @param tuples 
  * @throws IllegalTupleException
  * @throws TupleSpaceException
  */
 public void outg(ITuple[] tuples) throws TupleSpaceException
 {
  // previous parameter checking guarantees tuples (and its elements) not to be null 
  for (int i = 0; i < tuples.length; i++) if (isMatchAllTemplate(tuples[i])) throw new IllegalTupleException("outg of match-all-tuples template not allowed");	
  ts.outg(tuples);
 }
 
 /**
  * In. THIS METHOD IS NOT USED BY THE REACTOR.
  * 
  * @param template
  * @throws TupleSpaceException
  */
 public ITuple in(ITuple template) throws TupleSpaceException
 {
  throw new LimeError("invalid TupleSpaceProxy call");	
 }
 
 /**
  * Inp.
  * 
  * @param template
  * @return result
  * @throws TupleSpaceException
  */
 public ITuple inp(ITuple template) throws TupleSpaceException
 {
  // previous parameter checking guarantees template not to be null	
  ITuple result = null;	
  if (!isMatchAllTemplate(template)) 
	  result = ts.inp(template);
  else
  { 
   result = filterOne(template, ts.rdg(MATCHALL));
   // please note that we remove exactly the filtered tuple (an inp using a limified tuple must match TupleID)
   if (result != null) ts.inp(result);
  }
  return result;
 }

 /**
  * Ing.
  * 
  * @param template
  * @return results
  * @throws TupleSpaceException
  */
 public ITuple[] ing(ITuple template) throws TupleSpaceException
 {
  // previous parameter checking guarantees template not to be null
  ITuple[] result = null;	
  if (!isMatchAllTemplate(template)) result = ts.ing(template);
  else 
  {
   result = filterAll(template, ts.rdg(MATCHALL));
   // please note that we remove exactly the filtered tuples (an inp using a limified tuple must match TupleID)
   if (result != null) for (int i = 0; i < result.length; i++) ts.inp(result[i]);
  }
  return result;
 }

 /**
  * Rd. THIS METHOD IS NOT USED BY THE REACTOR.
  * 
  * @param template 
  * @throws TupleSpaceException
  */
 public ITuple rd(ITuple template) throws TupleSpaceException
 {
  throw new LimeError("invalid TupleSpaceProxy call");	
 }
 
 /**
  * Rdp.
  * 
  * @param template
  * @return result
  * @throws TupleSpaceException
  */
 public ITuple rdp(ITuple template) throws TupleSpaceException
 {
  // previous parameter checking guarantees template not to be null	
  if (!isMatchAllTemplate(template)) return ts.rdp(template);
                                else return filterOne(template, ts.rdg(MATCHALL));
 }

 /**
  * Rdg.
  * 
  * @param template
  * @return results
  * @throws TupleSpaceException
  */
 public ITuple[] rdg(ITuple template) throws TupleSpaceException
 {
  // previous parameter checking guarantees template not to be null	
  if (!isMatchAllTemplate(template)) return ts.rdg(template);	
                                else return filterAll(template, ts.rdg(MATCHALL));
 }

 /**
  * Defines the matching method for limified ITuples. 
  * If the ID, cur and dest IFields of the two ITuples don't match, false is immediately returned. Else, if the 
  * template has no other IFields, true is returned. Otherwise we check the remaining IFields to determine equality.
  * This method (is the only one that) relies on the method "matches" of class lights.adapters.Tuple.
  */
 static boolean matchTuples(ITuple template, ITuple tuple)
 {	
  if (!isMatchAllTemplate(template)) return template.matches(tuple);	 
  else
  {
   ITuple aux = LimeTupleSpace.copy(tuple, 0, LimeConstants.TAGS_NUMBER);
   return template.matches(aux);
  }
 }

 /**
  * Given an array of tuples, this method returns one of them that matches the template, if any. This method relies 
  * on the method "matchTuples" defined in this class.
  * 
  * @param template
  * @param array of tuples
  * @return a tuple in the array that matches the template
  */
 private static ITuple filterOne(ITuple template, ITuple[] tuples)
 {
  if (tuples == null) return null;
  int i = 0;
  while (i < tuples.length) if (matchTuples(template, tuples[i])) return tuples[i]; else i++;
  return null;
 }
 
 /**
  * Given an array of tuples, this method returns all the tuples in the array that match the template, if any. This 
  * method relies on the method "matchTuples" defined in this class.
  * 
  * @param template
  * @param array of tuples
  * @return all the tuples in the array that match the template
  */
 private static ITuple[] filterAll(ITuple template, ITuple[] tuples)
 {
  if (tuples == null) return null;	
  ITuple[] result = null;	
  Vector v = new Vector();
  int i = 0;
  while (i < tuples.length) { if (matchTuples(template, tuples[i])) v.addElement(tuples[i]); i++; }
  if (v.size() != 0) 
     {
  	  result = new ITuple[v.size()];
      v.copyInto(result);
     }
  return result;
 }
 
}
